package top.lww0511.extra.interceptor;

import com.baomidou.mybatisplus.core.conditions.Condition;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.springframework.util.StringUtils;
import top.lww0511.extra.annotation.One2Many;
import top.lww0511.extra.utils.MpExtraUtil;
import top.lww0511.extra.utils.SpringBeanFactoryUtils;
import java.lang.reflect.Field;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Properties;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;

/**
 * @author lww
 */
@Intercepts({
        @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@Slf4j
public class One2ManyInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object result = invocation.proceed();
        if (result == null) {
            return null;
        }
        if (result instanceof ArrayList) {
            ArrayList list = (ArrayList) result;
            for (Object o : list) {
                handleOne2ManyAnnotation(o);
            }
        } else {
            handleOne2ManyAnnotation(result);
        }
        return result;
    }

    @SneakyThrows
    private void handleOne2ManyAnnotation(Object o) {
        Class<?> aClass = o.getClass();
        String tableName = MpExtraUtil.humpToUnderline(MpExtraUtil.first2Lower(aClass.getSimpleName()));
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            One2Many one2Many = field.getAnnotation(One2Many.class);
            if (one2Many != null) {
                //获取list中的真实对象的类型
                Type type = field.getGenericType();
                ParameterizedType pt = (ParameterizedType) type;
                Class<?> realClass = (Class<?>) pt.getActualTypeArguments()[0];
                String filedName = MpExtraUtil.first2Lower(realClass.getSimpleName());
                String self = one2Many.self();
                Object value = MpExtraUtil.getValue(o, self);
                String as = one2Many.as();
                if (StringUtils.isEmpty(as)) {
                    //关联表字段
                    as = tableName + "_id";
                }
                Class<? extends BaseMapper> mapper = one2Many.mapper();
                BaseMapper baseMapper;
                if ("BaseMapper".equalsIgnoreCase(mapper.getSimpleName())) {
                    Object bean = null;
                    try {
                        bean = SpringBeanFactoryUtils.getApplicationContext().getBean(filedName + "Mapper");
                    } catch (Exception ignored) {
                    }
                    if (bean == null) {
                        try {
                            bean = SpringBeanFactoryUtils.getApplicationContext().getBean(filedName + "Dao");
                        } catch (Exception ignored) {
                        }
                    }
                    if (bean == null) {
                        throw new IllegalArgumentException(filedName + " Mapper命名不规范,请以 实体类名 + Mapper 或者 实体类名 + Dao 命名 Mybatis 文件");
                    }
                    baseMapper = (BaseMapper) bean;
                } else {
                    baseMapper = SpringBeanFactoryUtils.getApplicationContext().getBean(mapper);
                }
                QueryWrapper<Object> eq = Condition.create().eq(as, value);
                Object list = baseMapper.selectList(eq);
                field.set(o, list);
            }
        }
    }

    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

}
